Redis 基于 RedisJSON 的缓存方案
方案介绍
数据选型
首先声明:以下我要介绍的这套缓存方案,它的缓存数据是基于 RedisJSON 实现的。之所以选择 RedisJSON 而非传统的 String (Plain KV),本质上是从把 Redis 当成一个简单的缓存盒子进化到了 把 Redis 当成一个可索引的文档数据库。这使得它具备如下的核心优势:
- 文档局部更新能力:普通 KV 如果你只想修改 EmployeeDoc 里的 salary 字段,你必须先 GET 整个对象,反序列化,修改值,再 SET 回去。这存在并发覆盖风险(ABA问题)且浪费带宽。而 RedisJSON 可以直接执行 “JSON.SET key $.salary 5000”。它是原子性的局部更新,只传输改变的字段,性能极高。
- 深度查询与索引(与RediSearch 联动):普通 KV 缓存是黑盒。除非你知道确定的 Key,否则无法搜出 “所有工资 > 5000 的员工”,除非你把所有数据拉到 Java 内存里过滤。而 RedisJSON 配合 RediSearch,可以直接对 JSON 内部的属性建立索引。你可以像 SQL 一样查询缓存:FT.SEARCH idx:emp “@salary:[5000 +inf]”。
- 减少应用端的序列化负担:普通 KV 应用端每次读写都要进行繁重的 JSON 序列化/反序列化(Jackson/Fastjson)。RedisJSON 数据以二进制树状结构存在 Redis 内存中。Redis 负责解析路径,应用端在很多场景下可以直接获取部分片段,减轻了 CPU 压力。
当然,任何事情都是有代价的,使用 RedisJSON 的代价是:
- 内存开销稍大:普通 String 只是存储原始字节。RedisJSON 为了支持快速路径访问,会在内存中维护一个树状结构(JAX解析树)。这通常比纯 String 存储多消耗 10% - 30% 的内存。
- 模块依赖性:Redis 原生并不自带 JSON 功能。你必须确保你的 Redis 环境安装了 RedisStack 或手动加载了 RedisJSON 模块。这在某些受到严格限制的云托管 Redis 服务上可能无法使用。
- 客户端选型限制:传统的 Jedis 或老版本的 Lettuce 对 RedisJSON 指令支持并不全。需要像你定制化开发,使用 masterRedisTemplate.execute() 配合 Lua 脚本或者专门的客户端驱动。
技术设计
这套缓存架构的设计哲学是 非侵入式的防御性缓存架构,其核心逻辑可以用 1-2-3 模式 概括:
1 个核心目标:在保障 RedisJSON 高性能检索的同时,维护数据的一致性与 DB 的安全性。
2 个解耦切面:
- 读切面 (Reading):策略为 Cache-Aside。内置 “滑动窗口续期” 与 “双重检查锁定”。
- 写切面 (Writing):策略为 Write-Behind / Evict。支持异步双写与强制失效。
3 重防御机制:
- 防穿透:空对象占位符隔离无效请求。
- 防击穿:分段锁控制回源频率。
- 防雪崩:通过不同方法的 TTL 配置实现过期时间微扰。
此外关于并发安全,我放弃了性能较差的 synchronized(this),也没有使用可能导致内存风险的 synchronized (redisKey.intern()),而是引入了 Guava 的 Striped 分段锁 来实现 key 级别的细粒度并发控制,这让系统在热点数据失效时,依然能保持个位数的 DB 回源压力。
关于性能优化,我利用了 Redis Lua 脚本将 JSON.SET 和 EXPIRE 封装为原子操作,并通过 StringRedisTemplate 解决了 Spring Data Redis 常见的二次序列化(转义引号)问题。
这套方案是完全声明式的。业务开发人员只需要在 Service 方法上挂一个注解,配置 SpEL 表达式,所有的缓存同步、异步回填、异常重试逻辑都会在切面中自动完成,代码侵入性几乎为零。
好了,废话不多少,直接上菜。
核心代码
缓存切面
写缓存切面
主要功能:处理数据的增删改,数据新增时同步缓存,数据更新或删除时移除过期缓存
1 | import com.demo.componet.DynamicRedisJsonTemplate; |
读缓存切面
主要功能:处理数据的读取,先读缓存数据,缓存中不存在则查库,并且将查询结果缓存起来。
1 | import com.demo.componet.DynamicRedisJsonTemplate; |
SpringEL支持
1 | import org.aspectj.lang.JoinPoint; |
Lua 脚本支持
1 | /** |
核心应用组件
RedisJsonWritingCache:
1 | /** |
RedisJsonReadingCache:
1 | /** |
相关测试代码
实体演示类
1 |
|
业务演示类
1 | /** |
压测单元
1 | /** |